package logica.util;

import java.io.Serializable;
import java.util.Calendar;
import java.util.GregorianCalendar;


/**
 * Objetos que encapsulam datas.
 * @author Localisa (Usa quem precisa!)
 */
public class Data implements Serializable{
	
	private transient static final long serialVersionUID = 1L;
	private int dia;
	private Mes mes;
	private int ano;
	private GregorianCalendar calendario;
	
	
	/**
         * Enum correspondente aos meses do ano.
	 * @author Localisa (Usa quem precisa!)
	 */
	public enum Mes {
		JANEIRO("janeiro",1, 31), 
		FEVEREIRO("fevereiro",2, 28, 29),
		MARCO("marco",3, 31),
		ABRIL("abril",4, 30),
		MAIO("maio",5, 31),
		JUNHO("junho",6, 30),
		JULHO("julho",7, 31),
		AGOSTO("agosto",8, 31),
		SETEMBRO("setembro",9, 30),
		OUTUBRO("outubro",10, 31),
		NOVEMBRO("novembro",11, 30),
		DEZEMBRO("dezembro",12, 31);
		
		private int diasMes;
		private int numero;
		private int diasMesAnoBissexto;
		private String extenso;
		private Mes(String extenso, int numero, int diasMes){
			this.extenso = extenso;
			this.numero = numero;
			this.diasMes = diasMes;
		}
		
		/**
		 * @param O nome do Mes por extenso
		 * @param O numero do mes no calendario
		 * @param O numero de dias do mes
		 * @param O numero de dias do mes no ao bissexto (apenas para Fevereiro)
		 */
		private Mes(String extenso, int numero, int mes, int mesAnoBissexto){
			this.extenso = extenso;
			this.numero = numero;
			this.diasMes = mes;
			this.diasMesAnoBissexto = mesAnoBissexto;
		};
		
		/**
		 * 
		 * @return A quantidade de dias que o mes correspondente possui
		 */
		public int getMes(){
			return diasMes;
		}
		
		/**
		 * @return A quantidade de dias quando esta num ano bissexto (somente aplicavel ao mes de fevereiro)
		 */
		public int getDiasMesAnoBissexto(){
			return diasMesAnoBissexto;
		}
		
		/**
		 * 
		 * @return O numero correspondente ao mes
		 */
		public int getNumero(){
			return this.numero;
		}
		
		/**
		 * 
		 * @return O nome do mes por extenso
		 */
		public String getExtenso(){
			return this.extenso;
		}
	};
	
	/**
	 * Construtor sem parametro seta os valores para a data atual
	 */
	public Data(){
		//Seta para o dia atual
		calendario = new GregorianCalendar();
		dia= calendario.get(Calendar.DAY_OF_MONTH);  
		//Seta para o mes atual
		mes = Mes.values()[calendario.get(Calendar.MONTH)];
		//Seta ano para o atual
		ano = calendario.get(Calendar.YEAR);
		//Construtor sem parametros setar data para data atual
	}
	
	
	/**
	 * Constroi com os valores que voce passar como parametro
	 * @param dia O dia da data
	 * @param mes O mes da data
	 * @param ano O ano da data
	 */
	public Data(int dia, int mes, int ano) throws Exception{
		
		calendario = new GregorianCalendar();
		
		if(dia >= 1 && dia <= 31){
			if(mes >= 1 && mes <= 12){
				if(ano > 1900 && ano<=9999){
					calendario.set(Calendar.DAY_OF_MONTH, dia);
					calendario.set(Calendar.MONTH, mes - 1);
					calendario.set(Calendar.YEAR, ano);
					
					this.dia = calendario.get(Calendar.DAY_OF_MONTH);
					this.mes = Mes.values()[calendario.get(Calendar.MONTH)];
					
					this.ano = calendario.get(Calendar.YEAR);
				}else{
					throw new Exception("\n \t <Data invalida!>");
				}
			}else{
				throw new Exception("\n \t <Data invalida!>");
			}
		}else{
			throw new Exception("\n \t <Data invalida!>");
		}
	}
	
	
	/**
	 * Do mesmo modo que o anterior so que mes pode ser o enum publico Mes
	 * @param dia O dia da data
	 * @param Mes O mes da data
	 * @param ano O ano da data
	 */
	public Data(int dia, Mes mes, int ano) throws Exception{
		this(dia, mes.getNumero(), ano);
	}
	
	/**
	 * @return O dia da data
	 */
	public int getDia() {
		return dia;
	}
	
	
	/**
	 * Faz o calculo para verificar se um ano eh bissexto
	 * @param ano O ano da data
	 * @return Caso o ano seja bissexto retorna true, caso nao false
	 */
	public boolean anoEhBissexto(int ano){
		if(ano > 0){
			return (ano%4 == 0 && (ano%400 ==0 || ano%100 != 0));
		}
		else{
			return false;
		}
	}
	
	
	/**
	 * Pega o ano em que esta setada a data para fazer o calculo se eh bissexto ou nao
	 * @return True se o ano eh bissexto e False caso contrario
	 */
	public boolean anoEhBissexto(){
		return this.anoEhBissexto(this.ano);
	}
	
	
	/**
	 * Configura o dia do calendario
	 * @param dia O dia do mes
	 */
	public void setDia(int dia) throws Exception{
		if(dia > 0 && dia <= mes.getMes()){
			calendario.set(Calendar.DAY_OF_MONTH, dia);
			this.dia = calendario.get(Calendar.DAY_OF_MONTH);
		}
		
		else{
			throw new Exception("\n \t <Data invalida!>");
		}
	}
	
	
	/**
	 * @return A constante Mes correspondente ao mes setado 
	 */
	public Mes getMes() {
		return mes;
	}
	
	/**
	 * Configura o mes com um inteiro
	 * @param mes O novo mes da data
	 */
	public void setMes(int mes) throws Exception{
		if(mes > 0 && mes <= Constantes.MESES_DO_ANO){
			calendario.set(Calendar.MONTH, mes - 1);
			this.mes = Mes.values()[calendario.get(Calendar.MONTH)];
		}
		else{
			throw new Exception("\n \t <Data invalida!>");
		}
	
	}
	/**
	 * Configura o mes com um enum Mes
	 * @param Mes - mes
	 */
	public void setMes(Mes mes) throws Exception{
		if(mes != null){
			this.setMes(mes.getNumero());
		}else{
			throw new Exception("\n \t <Data invalida!>");
		}
	}
	
	/**
	 * 
	 * @return O ano da data
	 */
	public int getAno() {
		return ano;
	}
	
	
	/**
	 * Seta o ano para um valor acima de 1900, caso seja passado um ano menor que 1900 o ano nao sera modificado
	 * @param ano O novo ano da data
	 */
	public void setAno(int ano) throws Exception{
		if(ano > Constantes.ANO_MINIMO){
			calendario.set(Calendar.YEAR, ano);
			this.ano = calendario.get(Calendar.YEAR);
		}
		else{
			throw new Exception("\n \t <Data invalida!>");
		}
	}
	
	
	/**
	 * @return A data no formato dd/mm/aaaa
	 */
	public String getData(){
		return dia+"/"+mes.getNumero()+"/"+ano;
	}
	

	/**
	 * Retorna uma string no formato <dia> de <mes por extenso> de <ano>
	 * @return String 
	 */
	public String getDataPorExtenso(){
		return dia+" de "+mes.getExtenso()+" de "+ano;
	}
	
	
	/**
	 * @return Mostra uma string no formato dd/mm/aaaa
	 */
	public String toString(){
		return this.getData();
	}
	
	
	/**
	 * Compara um objeto Data com outro
	 * @return True se o dia, o mes e o ano forem iguais e False caso contrario
	 */
	public boolean equals(Object data){
		if(data instanceof Data){
			return ((Data) data).getAno() == this.ano && ((Data) data).getMes().equals(this.mes) && ((Data) data).getDia() == this.dia;
		}else{
			return false;
		}
	}//Fim do equals
	
	
	/**
	 * @return Um calendario gregoriano 
	 */
	public Calendar getCalendario(){
		return this.calendario;
	}
	
	/**
	 * @param atual A data atual
	 * @param futura A data futura
	 * @return O valor que indica a diferenca entre as datas em dias
	 * @throws Exception Se a data futura for mais recente que a atual
	 */
	
	public static int getDiferencaDeDiasEntreDatas(Data atual, Data futura) {
            GregorianCalendar calendarFutura = new GregorianCalendar(futura.getAno() , futura.getMes().ordinal(), futura.getDia());
            GregorianCalendar calendarAtual = new GregorianCalendar(atual.getAno() , atual.getMes().ordinal(), atual.getDia());

            return (int) (calendarFutura.getTime().getTime() - calendarAtual.getTime().getTime()) / (1000*60*60*24);
        }
}